﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;


namespace ProviderLogging
{
    public class Logging
    {

        public static void LogOperation(string method, object[] parameters, object result, Exception excpt)
        {
            StringBuilder sb = new StringBuilder();

            StackTrace st = new StackTrace();
            StackFrame[] frames = st.GetFrames();
            StackFrame frame = frames[1];
            MethodBase methodBase = frame.GetMethod();

            BuildStringPrefix(sb, System.Reflection.Assembly.GetExecutingAssembly(), methodBase.DeclaringType.Name, method);

            sb.Append('(');

            if (parameters != null)
            {
                for (int looper = 0; looper < parameters.Length; looper++)
                {
                    sb.Append(GetParmStringValue(parameters[looper]));
                    if (looper != parameters.Length - 1) sb.Append(", ");
                }
            }
            sb.Append(')');
            if (result != null)
            {
                sb.Append(", Result=").Append(GetParmStringValue(result));
            }
            if (excpt != null)
            {
                sb.Append(", Exception=").Append(excpt.ToString());
            }

            WriteStringDirect(sb.ToString());
        }

        public static void WriteStringDirect(string str)
        {
            System.Diagnostics.Trace.WriteLine(str);
        }

        public static void WriteString(string str)
        {
            StringBuilder sb = new StringBuilder();
            string mthdName = null;

            StackTrace st = new StackTrace();
            StackFrame[] frames = st.GetFrames();
            StackFrame frame = frames[1];
            MethodBase methodBase = frame.GetMethod();

            mthdName = methodBase.Name;
            BuildStringPrefix(sb, Assembly.GetExecutingAssembly(), methodBase.DeclaringType.Name, mthdName);
            sb.Append(str);
            WriteStringDirect(sb.ToString());
        }

        public static void WriteString(string str, Assembly asm, string className, string methodName)
        {
            StringBuilder sb = new StringBuilder();
            BuildStringPrefix(sb, asm, className, methodName);
            sb.Append(str);
            WriteStringDirect(sb.ToString());
        }

        public static void BuildStringPrefix(StringBuilder sb, Assembly asm, string className, string methodName)
        {
            string shortAsmName = asm.CodeBase.Substring(asm.CodeBase.LastIndexOf('/') + 1);
            sb.Append(shortAsmName).Append('~').Append(className).Append('!').Append(methodName).Append('~');
        }

        protected static string GetParmStringValue(object parm)
        {
            return (GetParmStringValue(parm, null));
        }
        protected static string GetParmStringValue(object parm, Hashtable tbl)
        {
            if (tbl == null) tbl = new Hashtable();
            if (parm == null) return "<<NULL>>";
            else
            {
                Type parmType = parm.GetType();
                if (!parmType.IsClass) return (parm.ToString());
                if (parmType == typeof(System.String)) return (string)parm;
                // TODO: Deal with things like NameValueCollection that return the keys when enumerated -- rather than a Key-Value-Pair type object
                if (parmType.GetInterface("System.Collections.IEnumerable") != null)
                {
                    // has ennumerable interface
                    StringBuilder output = new StringBuilder();
                    output.Append("{");
                    foreach (object obj in ((System.Collections.IEnumerable)(parm)))
                    {
                        output.Append(GetParmStringValue(obj)).Append(", ");
                    }
                    if (output.Length > 2) output.Remove(output.Length - 2, 2);
                    output.Append("}");

                    return (output.ToString());
                }
                // must be a class to be here, go get all of the properties
                PropertyInfo[] parmProperties = parmType.GetProperties();
                StringBuilder classOutput = new StringBuilder();
                classOutput.Append("{Class: ");
                foreach (PropertyInfo prop in parmProperties)
                {
                    // Determine if the object has been processed in this loop -- and if so skip it.
                    object propVal = prop.GetValue(parm, null);
                    classOutput.Append(".").Append(prop.Name).Append("=");
                    if (propVal == null) classOutput.Append("<<NULL>>");
                    else if (!tbl.ContainsKey(propVal.GetHashCode()))
                    {
                        classOutput.Append(GetParmStringValue(propVal, tbl));
                    }
                    else
                    {
                        classOutput.Append("<<RECURSIVE>>");
                    }
                    classOutput.Append(", ");
                }
                classOutput.Remove(classOutput.Length - 2, 2).Append("}");

                return (classOutput.ToString());
            }
        }
    }
}
